package edu.hawaii.myisern.action;

import java.util.Iterator;
import java.util.List;
import edu.hawaii.myisern.model.ListStringConverter;
import edu.hawaii.myisern.model.MyIsernModel;
import edu.hawaii.myisern.organizations.jaxb.AffiliatedResearchers;
import edu.hawaii.myisern.organizations.jaxb.Organization;
import edu.hawaii.myisern.organizations.jaxb.ResearchKeywords;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.DontValidate;
import net.sourceforge.stripes.action.RedirectResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.validation.SimpleError;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidationErrors;

/**
 * Web interface for the organizations page.
 * 
 * @author Edward Kim
 * @author JianFei Liao
 * @author KaYee Leung
 */
public class OrganizationsActionBean implements ActionBean {

  /** The name for the organization. */
  @Validate(field = "organizationName") private String organizationName;
  /** The Organization object instance. */
  private Organization organization;
  /** A String representation of the research keywords. */
  @Validate(field = "keywordsList") private String keywordsString;
  /** A String representation of the affiliated researchers. */
  private String reseachersString;
  /** A converter between String and list of String. */
  private ListStringConverter converter = new ListStringConverter();
  /** An invalid researcher link relates to the organization. */
  private String linkMessage;
  
  /** Required by the ActionBean interface. */
  private ActionBeanContext context;
  /** Our single MyIsern instance manipulated by all webapp users. */
  private MyIsernModel myModel = MyIsernModel.getInstance();

  /**
   * Returns the context. Required by the interface.
   * 
   * @return the ActionBeanContext.
   */
  public ActionBeanContext getContext() {
    return context;
  }

  /**
   * Sets the context. Required by the interface.
   * 
   * @param context The ActionBeanContext to set.
   */
  public void setContext(ActionBeanContext context) {
    this.context = context;
  }

  /**
   * Returns an organization.
   * 
   * @return An organization.
   */
  public Organization getOrganization() {
    return this.organization;
  }
  
  /**
   * Sets an organization
   * 
   * @param organization The organization that will be set to.
   */
  public void setOrganization(Organization organization) {
    this.organization = organization;
  }
  
  /**
   * Sets the organization's name.
   * 
   * @param name The organization name to set.
   */
  public void setOrganizationName(String name) {
    this.organizationName = name;
  }
  
  /**
   * Sets the affiliated researchers.
   * 
   * @param string The new affiliated researchers.
   */
  public void setResearchersString(String string) {
    this.reseachersString = string;
  }
  
  /**
   * Gets the affiliated researchers.
   * 
   * @return The String representation of the affiliated researchers.
   */
  public String getResearchersString() {
    List<String> list = this.organization.getAffiliatedResearchers().getAffiliatedResearcher();
    // convert the list to a String
    this.reseachersString = this.converter.listToString(list);
    return this.reseachersString;
  }
  
  /**
   * Sets the research keywords.
   * 
   * @param string The new research keywords.
   */
  public void setKeywordsString(String string) {
    this.keywordsString = string.trim();
  }
  
  /**
   * Gets the research keywords.
   * 
   * @return A String representation of the research keywords.
   */
  public String getKeywordsString() {
    List<String> list = this.organization.getResearchKeywords().getResearchKeyword();
    // convert the list to a String
    this.keywordsString = this.converter.listToString(list);
    return this.keywordsString;
  }
  
  /**
   * Sets the invalid researcher link.
   * 
   * @param string The new invalid researcher link.
   */
  public void setLinkMessage(String string) {
    this.linkMessage = string;
  }
  
  /**
   * Provides an iterator of the organizations to the page.
   * 
   * @return an iterator that will go through all the organizations
   */
  public Iterator<Organization> getOrganizationsIterator() {
    return this.myModel.getOrganizationsList().iterator();
  }

  /**
   * Shows information for all organizations.
   * 
   * @return A Resolution indicating to redisplay the organizations.jsp page.
   * @throws Exception error message
   */
  public Resolution showAllOrganizations() throws Exception {
    return new RedirectResolution("/organizations.jsp").flash(this);
  }
  
  /**
   * Populates the data for the organization before editing.
   * 
   * @return A resolution to the editOrganization page.
   */
  @DontValidate
  public Resolution preEdit() {
    this.organization = this.myModel
        .getSingleOrganization(this.organizationName.trim().replace(' ', '_'));
    return new RedirectResolution("/editOrganization.jsp").flash(this);
  }
  
  /**
   * Saves the changes for organization.
   * 
   * @throws Exception exception  
   * @return A new resolution to the organization page.
   */
  @DefaultHandler
  public Resolution saveOrganization() throws Exception {
    ValidationErrors errors = new ValidationErrors();
    // check if organization name is provided
    if (this.organization == null || this.organization.getName() == null) {
      errors.add("organizationName", new SimpleError("The organization name is required."));
      this.getContext().setValidationErrors(errors);
      return this.getContext().getSourcePageResolution();
    }
    // check for other data
    if (this.organization.getType() == null) {
      this.organization.setType("");
    }
    if (this.organization.getContact() == null) {
      this.organization.setContact("");
    }
    if (this.reseachersString == null) {
      // does nothing because this field will be constructed at below
      System.out.println("Affiliated researchers field will be constructed.");
    }
    if (this.organization.getCountry() == null) {
      this.organization.setCountry("");
    }
    if (this.keywordsString == null) {
      errors.add("keywordsList", new SimpleError("At least one research keyword is required."));
      this.getContext().setValidationErrors(errors);
      return this.getContext().getSourcePageResolution();
    }
    if (this.organization.getResearchDescription() == null) {
      this.organization.setResearchDescription("");
    }
    if (this.organization.getHomePage() == null) {
      this.organization.setHomePage("");
    }
    
    // construct the affiliated researchers field using the researchersStirng
    // convert the String back to a list
    List<String> researchersList = this.converter.stringToList(this.reseachersString);
    // create a new AffiliatedResearchers object with the list
    AffiliatedResearchers affiliatedResearchers = new AffiliatedResearchers();
    affiliatedResearchers.getAffiliatedResearcher().clear();
    affiliatedResearchers.getAffiliatedResearcher().addAll(researchersList);
    // sets the affiliated researchers in the Organization object
    this.organization.setAffiliatedResearchers(affiliatedResearchers);
    
    // construct the research keywords field using keywordsString
    // convert the String back to a list
    List<String> keywordsList = this.converter.stringToList(this.keywordsString);
    // create a new ResearchKeywords object with the list
    ResearchKeywords researchKeywords = new ResearchKeywords();
    researchKeywords.getResearchKeyword().clear();
    researchKeywords.getResearchKeyword().addAll(keywordsList);
    // sets the research keywords in the Organization object
    this.organization.setResearchKeywords(researchKeywords);
    
    // store the changes and write out to the XML file
    this.myModel.addOrEditOrganization(this.organization);
    this.myModel.save();
    return new RedirectResolution("/organizations.jsp").flash(this);
  }

  /**
   * Finds the organization that has the invalid researcher link.
   *
   * @return A new resolution of the editOrganization page.
   */
  public Resolution findOrganizationWithLink() {
    // parse the link message to find the organization
    int index = this.linkMessage.indexOf(':');
    String name = this.linkMessage.substring(index + 1).trim();
    String id = name.replace(' ', '_');
    this.organization = this.myModel.getOrganizationsMap().get(id); 
    return new RedirectResolution("/editOrganization.jsp").flash(this);
  }
}